package com.igoal.service;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;

import org.apache.commons.logging.LogFactory;
import org.appfuse.service.UserExistsException;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.security.providers.encoding.PasswordEncoder;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import com.igoal.dao.CategoryDAO;
import com.igoal.dao.HobbyDAO;
import com.igoal.dao.InfoQueueDAO;
import com.igoal.dao.JobDAO;
import com.igoal.dao.PrivacySettingDAO;
import com.igoal.dao.ProfileCompleteDAO;
import com.igoal.dao.RoleDAO;
import com.igoal.dao.UserActivityDAO;
import com.igoal.dao.UserCategoryDAO;
import com.igoal.dao.UserCheerDAO;
import com.igoal.dao.UserDAOExt;
import com.igoal.dao.UserFriendDAO;
import com.igoal.dao.UserGoalDAO;
import com.igoal.dao.UserGoalEntryDAO;
import com.igoal.dao.UserGoalEntryDoneDAO;
import com.igoal.dao.UserInfoDAO;
import com.igoal.dao.UserJobDAO;
import com.igoal.dao.UserLikeDAO;
import com.igoal.dao.UserSchoolDAO;
import com.igoal.dao.UserStatusDAO;
import com.igoal.dao.UserThoughtDAO;
import com.igoal.dao.UserThumbDAO;
import com.igoal.dao.WallUpdateDAO;
import com.igoal.dto.FriendDTO;
import com.igoal.dto.FriendRequestDTO;
import com.igoal.dto.JobDTO;
import com.igoal.dto.PotentialFriendDTO;
import com.igoal.dto.ProfileCompleteDTO;
import com.igoal.dto.SchoolDTO;
import com.igoal.dto.UserDTO;
import com.igoal.dto.UserStatusDTO;
import com.igoal.entity.ActionTypeEnum;
import com.igoal.entity.Category;
import com.igoal.entity.ComponentEnum;
import com.igoal.entity.DataStatus;
import com.igoal.entity.Hobby;
import com.igoal.entity.InfoQueueEntry;
import com.igoal.entity.Job;
import com.igoal.entity.PrivacyEnum;
import com.igoal.entity.PrivacySetting;
import com.igoal.entity.ProfileComplete;
import com.igoal.entity.Role;
import com.igoal.entity.School;
import com.igoal.entity.User;
import com.igoal.entity.UserCategory;
import com.igoal.entity.UserCategoryPk;
import com.igoal.entity.UserCheer;
import com.igoal.entity.UserFriend;
import com.igoal.entity.UserGoalDone;
import com.igoal.entity.UserGoalEntry;
import com.igoal.entity.UserInfo;
import com.igoal.entity.UserJob;
import com.igoal.entity.UserJobId;
import com.igoal.entity.UserLike;
import com.igoal.entity.UserSchool;
import com.igoal.entity.UserStatus;
import com.igoal.entity.UserThought;
import com.igoal.entity.UserThumb;
import com.igoal.entity.factory.EntityFactory;
import com.igoal.exception.LoginException;
import com.igoal.exception.NoContactServiceFoundException;
import com.igoal.utils.StringUtils;
import com.igoal.web.Constants;
import com.igoal.web.security.LoginStatus;

@Service("userManagerExt")
public class UserManagerExtImpl extends BaseService<UserDTO, User, Long>
		implements UserService, UserManagerExt {
	@Qualifier("userDAOExt")
	private UserDAOExt userDAOExt;
	private PasswordEncoder passwordEncoder;
	private InfoQueueDAO infoQueueDAO;
	private UserFriendDAO userFriendDAO;
	private UserStatusDAO userStatusDAO;
	private UserInfoDAO userInfoDAO;
	private RoleDAO roleDAO;
	private UserLikeDAO userLikeDAO;
	private HobbyDAO hobbyDAO;
	private ProfileCompleteDAO profileCompleteDAO;
	private UserCategoryDAO userCategoryDAO;
	private CategoryDAO categoryDAO;
	private JobDAO jobDAO;
	private UserJobDAO userJobDAO;
	private UserSchoolDAO userSchoolDAO;
	private MailService mailService;
	private UserThumbDAO userThumbDAO;
	private UserGoalEntryDAO userGoalEntryDAO;
	private UserGoalEntryDoneDAO userGoalEntryDoneDAO;
	private UserActivityDAO userActivityDAO;
	private UserCheerDAO userCheerDAO;
	private UserThoughtDAO userThoughtDAO;
	private PrivacySettingDAO privacySettingDAO;
	private UserGoalDAO userGoalDAO;
	private WallUpdateDAO wallUpdateDAO;

	private ActionLoggerService actionLoggerService;

	public UserManagerExtImpl() {
		logger = LogFactory.getLog(this.getClass());
	}

	@Autowired
	public void setHobbyDAO(HobbyDAO hobbyDAO) {
		this.hobbyDAO = hobbyDAO;
	}

	@Autowired
	public void setUserLikeDAO(UserLikeDAO userLikeDAO) {
		this.userLikeDAO = userLikeDAO;
	}

	@Autowired
	public void setUserStatusDAO(UserStatusDAO userStatusDAO) {
		this.userStatusDAO = userStatusDAO;
	}

	@Autowired
	public void setUserInfoDAO(UserInfoDAO userInfoDAO) {
		this.userInfoDAO = userInfoDAO;
	}

	@Autowired
	public void setInfoQueueDAO(InfoQueueDAO infoQueueDAO) {
		this.infoQueueDAO = infoQueueDAO;
	}

	@Autowired
	public void setUserDAOExt(UserDAOExt userDAOExt) {
		this.userDAOExt = userDAOExt;
	}

	@Autowired
	public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
		this.passwordEncoder = passwordEncoder;
	}

	@Autowired
	public void setUserFriendDAO(UserFriendDAO userFriendDAO) {
		this.userFriendDAO = userFriendDAO;
	}

	@Autowired
	public void setRoleDAO(RoleDAO roleDAO) {
		this.roleDAO = roleDAO;
	}

	@Autowired
	public void setProfileCompleteDAO(ProfileCompleteDAO profileCompleteDAO) {
		this.profileCompleteDAO = profileCompleteDAO;
	}

	@Autowired
	public void setUserCategoryDAO(UserCategoryDAO userCategoryDAO) {
		this.userCategoryDAO = userCategoryDAO;
	}

	@Autowired
	public void setCategoryDAO(CategoryDAO categoryDAO) {
		this.categoryDAO = categoryDAO;
	}

	@Autowired
	public void setJobDAO(JobDAO jobDAO) {
		this.jobDAO = jobDAO;
	}

	@Autowired
	public void setUserJobDAO(UserJobDAO userJobDAO) {
		this.userJobDAO = userJobDAO;
	}

	@Autowired
	public void setUserSchoolDAO(UserSchoolDAO userSchoolDAO) {
		this.userSchoolDAO = userSchoolDAO;
	}

	@Autowired
	public void setUserThumbDAO(UserThumbDAO userThumbDAO) {
		this.userThumbDAO = userThumbDAO;
	}

	@Autowired
	public void setUserGoalEntryDAO(UserGoalEntryDAO userGoalEntryDAO) {
		this.userGoalEntryDAO = userGoalEntryDAO;
	}

	@Autowired
	public void setUserGoalEntryDoneDAO(
			UserGoalEntryDoneDAO userGoalEntryDoneDAO) {
		this.userGoalEntryDoneDAO = userGoalEntryDoneDAO;
	}

	@Autowired
	public void setUserActivityDAO(UserActivityDAO userActivityDAO) {
		this.userActivityDAO = userActivityDAO;
	}

	@Autowired
	public void setMailService(MailServiceImpl mailService) {
		this.mailService = mailService;
	}

	@Autowired
	public void setActionLoggerService(ActionLoggerService actionLoggerService) {
		this.actionLoggerService = actionLoggerService;
	}

	@Autowired
	public void setUserCheerDAO(UserCheerDAO userCheerDAO) {
		this.userCheerDAO = userCheerDAO;
	}
	
	@Autowired
	public void setUserThoughtDAO(UserThoughtDAO userThoughtDAO) {
		this.userThoughtDAO = userThoughtDAO;
	}
	
	@Autowired
	public void setPrivacySettingDAO(PrivacySettingDAO privacySettingDAO) {
		this.privacySettingDAO = privacySettingDAO;
	}
	
	@Autowired
	public void setUserGoalDAO(UserGoalDAO userGoalDAO) {
		this.userGoalDAO = userGoalDAO;
	}

	@Autowired
	public void setWallUpdateDAO(WallUpdateDAO wallUpdateDAO) {
		this.wallUpdateDAO = wallUpdateDAO;
	}

	public long validateEmail(String email) {
		if (email == null || email.trim().equals(""))
			throw new IllegalArgumentException("Empty email argument");

//		DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
//		criteria.add(Restrictions.eq("email", email));
//		List<User> list = userDAOExt.findByCriteria(criteria);
//		Assert.isTrue(list.size() <= 1);
//		if (list.size() > 0) {
//			User user = list.get(0);
//			return user.getId().longValue();
//		} else {
//			return 0l;
//		}
		return userDAOExt.existsEmail(email) ? 1l : 0l;
	}

	public long validateUsername(String username) {
//		DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
//		criteria.add(Restrictions.eq("username", username));
//		List<User> list = userDAOExt.findByCriteria(criteria);
//		Assert.isTrue(list.size() <= 1);
//		if (list.size() > 0) {
//			User user = list.get(0);
//			return user.getId().longValue();
//		} else {
//			return 0l;
//		}
		return userDAOExt.existsUsername(username) ? 1l : 0l;
	}

	private void setDefaultUserInformation(User user) {
		UserInfo userInfo = user.getUserInfo();
		int day = user.getDay();
		int month = user.getMonth();
		int year = user.getYear();

		Calendar cal = Calendar.getInstance();
		cal.set(year, month - 1, day);
		Date dob = cal.getTime();

		if (userInfo == null)
			userInfo = new UserInfo();
		userInfo.setBirthday(dob);

		if (userInfo.getCreateDate() == null)
			userInfo.setCreateDate(new Date());

		if (userInfo.getUpdateDate() == null)
			userInfo.setUpdateDate(new Date());

		if (userInfo.getStatus() == null)
			userInfo.setStatus(DataStatus.USING);

		user.setNumberFollowed(0);
		user.setNumberFollows(0);
		user.setNumberFriends(0);
		user.setNumberGoals(0);
		user.setEnabled(true);// TODO: remove on release

		userInfo.setUser(user);
		user.setUserInfo(userInfo);
	}

	private String getMD5Encode(String text) {

		byte[] defaultBytes = text.getBytes();
		try {
			MessageDigest algorithm = MessageDigest.getInstance("MD5");
			algorithm.reset();
			algorithm.update(defaultBytes);
			byte messageDigest[] = algorithm.digest();

			StringBuffer hexString = new StringBuffer();
			for (int i = 0; i < messageDigest.length; i++) {
				hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
			}
			return hexString.toString();
		} catch (NoSuchAlgorithmException nsae) {
			return null;
		}
	}

	private void generateDefaultData(User user) {
		setDefaultUserInformation(user);
		user.setActivationCode(getMD5Encode(user.getUsername()));
		setAuditDate(user);
	}

	/**
	 * {@inheritDoc}
	 */
	@Deprecated
	public User saveUser(User user) throws UserExistsException {

		if (user.getVersion() == null) {
			// if new user, lowercase userId
			user.setUsername(user.getUsername().toLowerCase());
		}

		// Get and prepare password management-related artifacts
		boolean passwordChanged = false;
		if (passwordEncoder != null) {
			// Check whether we have to encrypt (or re-encrypt) the password
			if (user.getVersion() == null) {
				// New user, always encrypt
				passwordChanged = true;
			} else {
				// Existing user, check password in DB
				String currentPassword = userDAOExt.getUserPassword(user.getUsername());
				if (currentPassword == null) {
					passwordChanged = true;
				} else {
					if (!currentPassword.equals(user.getPassword())) {
						passwordChanged = true;
					}
				}
			}

			// If password was changed (or new user), encrypt it
			if (passwordChanged) {
				user.setPassword(passwordEncoder.encodePassword(user.getPassword(), null));
			}
		} else {
			logger.warn("PasswordEncoder not set, skipping password encryption...");
		}

		try {
			generateDefaultData(user);
			userDAOExt.saveUser(user);
			return user;
		} catch (DataIntegrityViolationException e) {
			logger.warn(e.getMessage());
			throw new UserExistsException("User '" + user.getUsername() + "' already exists!");
		} catch (JpaSystemException e) { // needed for JPA
			logger.warn(e.getMessage());
			throw new UserExistsException("User '" + user.getUsername() + "' already exists!");
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void removeUser(String userId) {
		debug("removing user: " + userId);
		userDAOExt.remove(new Long(userId));
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @param username
	 *            the login name of the human
	 * @return User the populated user object
	 */
	public UserDTO getUserByUsername(String username) {
		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			UserDTO userDTO = new UserDTO();
			userDTO.initFrom(user);
			return userDTO;
		} catch (UsernameNotFoundException ex) {
			error(ex);
			return null;
		}
	}

	public List<UserDTO> searchUser(String text) {
		List<User> result = userDAOExt.searchUsers(text, -1);
		List<UserDTO> userDTOs = new ArrayList<UserDTO>();
		convertListEntitiesToDTOs(result, userDTOs, UserDTO.class);
		return userDTOs;
	}
	
	public List<PotentialFriendDTO> searchFriend(String username, String keyword, int page){
		try{
			User currentUser = (User) userDAOExt.loadUserByUsername(username);
			List<User> result = userDAOExt.searchUsers(keyword, page, username);
			List<PotentialFriendDTO> userDTOs = new ArrayList<PotentialFriendDTO>();
			for(User potentialFriend : result){
				if(potentialFriend.equals(currentUser)){
					continue;
				}
				PotentialFriendDTO dto = new PotentialFriendDTO();
				dto.initFrom(potentialFriend);
				List<FriendDTO> mutualFriends = getMutualFriends(currentUser, potentialFriend);
				dto.setMutualFriends(mutualFriends);
				dto.setFriend(userFriendDAO.isFriend(currentUser, potentialFriend));
				if(mutualFriends != null){
					dto.setCountMutualFriends(mutualFriends.size());
				}
				
				userDTOs.add(dto);
			}
			return userDTOs;
		}catch (Exception e) {
			logger.error(e);
			return null;
		}
	}
	
	public boolean checkSearchFriendResult(String username, String keyword, int page){
		return userDAOExt.hasSearchResult(keyword, page, username);
	}
	
	public int countSearchResult(String username, String keyword){
		return userDAOExt.countSearchResult(keyword, username);
	}

	public UserDTO login(String username, String password)
			throws LoginException {
		UserDTO user = null;
		try {
			user = getUserByUsername(username);
		} catch (Exception e) {

		}
		if (user == null) {
			debug("User is not exist.");
			throw new LoginException(LoginException.USER_NOT_EXIST);
		} else if (user.isAccountLocked()) {
			debug("Account is locked.");
			throw new LoginException(LoginException.ACCOUNT_LOCKED);
		} else if (!user.isEnabled()) {
			debug("Account is disable.");
			throw new LoginException(LoginException.ACCOUNT_DISABLE);
		} else if (passwordEncoder == null) {
			debug("System error - No password encoder.");
			throw new LoginException(LoginException.NO_PASSWORD_ENCODER);
		} else {
			String md5Password = passwordEncoder.encodePassword(password, null);
			if (md5Password.equals(user.getPassword())) {
				debug("User[" + user.getUsername() + "-" + user.getId()
						+ "] has logined.");
				return user;
			} else {
				debug("Password is not match.");
				throw new LoginException(LoginException.WRONG_PASSWORD);
			}
		}
	}

	public boolean exists(Long id) {
		return userDAOExt.exists(id);
	}

	public UserDTO get(Long id) {
		User user = userDAOExt.get(id);
		UserDTO userDTO = new UserDTO();
		userDTO.initFrom(user);
		return userDTO;
	}

	public List<UserDTO> getAll() {
		List<User> users = userDAOExt.getAllDistinct();
		List<UserDTO> userDTOs = new ArrayList<UserDTO>();
		for (User user : users) {
			UserDTO userDTO = new UserDTO();
			userDTO.initFrom(user);
			userDTOs.add(userDTO);
		}
		return userDTOs;
	}

	public void remove(Long id) {
		userDAOExt.remove(id);
	}

	public UserDTO getUser(String userId) {
		try {
			User user = userDAOExt.get(Long.parseLong(userId));
			UserDTO userDTO = new UserDTO();
			userDTO.initFrom(user);
			return userDTO;
		} catch (NumberFormatException ex) {
			error(ex);
			return null;
		}
	}

	public List<UserDTO> getUsers() {
		List<User> users = userDAOExt.getAllDistinct();
		List<UserDTO> userDTOs = new ArrayList<UserDTO>();
		convertListEntitiesToDTOs(users, userDTOs, UserDTO.class);
		return userDTOs;
	}

	public List<UserDTO> getUsersExceptForMe() {
		List<UserDTO> userDTOs = searchUser("");
		return userDTOs;
	}

	public UserDTO saveUser(UserDTO userDTO) throws UserExistsException {
		if(passwordEncoder == null){
			logger.warn("passwordEncoder is not alive");
			return null;
		}
		
		String encryptedPassword = passwordEncoder.encodePassword(userDTO.getPassword(), null);
		String activationCode = getMD5Encode(userDTO.getUsername()+userDTO.getPassword()+"qwertasdgzxcv");
		User user = EntityFactory.createRegisterUser(userDTO, encryptedPassword, activationCode);

		try {
			user = userDAOExt.saveUser(user);			
		} catch (DataIntegrityViolationException e) {
			logger.warn(e.getMessage());
			throw new UserExistsException("User '" + user.getUsername() + "' already exists!");
		} catch (JpaSystemException e) { // needed for JPA
			logger.warn(e.getMessage());
			throw new UserExistsException("User '" + user.getUsername() + "' already exists!");
		}
		userDTO.initFrom(user);
		return userDTO;
	}

	public UserDTO save(UserDTO object) {
		try {
			return saveUser(object);
		} catch (UserExistsException e) {
			logger.error(e);
			return null;
		}
	}

	public List<UserDTO> findContacts(ContactVendorEnum vendor,
			String username, String password) {
		try {
			ContactService service = ContactServiceFactory.getInstance().getContactService(vendor);
			List<UserDTO> users = service.listContact(username, password);
			for (UserDTO user : users) {
				if (user.getEmail() == null
						|| user.getEmail().trim().equals(""))
					continue;
				long userId = validateEmail(user.getEmail());
				if (userId > 0) {
					user.setId(userId);
				} else {
					// put unknown email to system
					try {
						User invite = (User) userDAOExt.loadUserByUsername(getUserInSession());
						addUnknownUserToQueue(invite, user);
					} catch (UsernameNotFoundException e) {
						// TODO: get admin account
						logger.error(e);
					}
				}
			}
			return users;
		} catch (NoContactServiceFoundException e) {
			error("Cannot find service: " + vendor.toString());
		}
		return new ArrayList<UserDTO>();
	}

	/**
	 * Add name and email of user to queue
	 * 
	 * @param userDTO
	 */
	public void addUnknownUserToQueue(User invite, UserDTO userDTO) {
		// check exist in queue first
		// if no add to queue
		// if yes -> has in queue but hasn't been user yet, toggle flag allow
		// send mail remind

		try {
			DetachedCriteria dc = DetachedCriteria.forClass(InfoQueueEntry.class);
			dc.add(Restrictions.eq("email", userDTO.getEmail()));
			List<InfoQueueEntry> list = infoQueueDAO.findByCriteria(dc);
			Assert.isTrue(list.size() <= 1);
			InfoQueueEntry entry;
			if (list.size() == 0) {
				entry = new InfoQueueEntry();
				entry.setEmail(userDTO.getEmail());
				entry.setCallName(userDTO.getDisplayName());
				entry.setCreateDate(new Date());
				entry.setUpdateDate(new Date());
				entry.setInvite(invite);
			} else {
				entry = list.get(0);
				entry.setReady(true);
				entry.setUpdateDate(new Date());
			}
			infoQueueDAO.save(entry);
		} catch (Exception e) {
			logger.error(e);
		}
	}

//	public Long register(UserDTO newUser) {
//		try {
//			if (StringUtils.isContainingUnicodeOrSpecialChar(newUser.getUsername()))
//				return new Long(SaveResult.FAIL);
//
//			if (StringUtils.hasSpaceInside(newUser.getUsername()))
//				return new Long(SaveResult.FAIL);
//
//			UserDTO registeredUser = saveUser(newUser);
//			if(registeredUser == null){
//				return new Long(SaveResult.FAIL);
//			}
//			
//			User user = userDAOExt.get(registeredUser.getId());
//
//			try {
//				Role role = roleDAO.getRoleUser();
//				UserRole userRole = EntityFactory.createUserRole(user, role);
//				userRoleDAO.save(userRole);
//				userInfoDAO.save(user.getUserInfo());
//
//				actionLoggerService.log(user, user, ActionTypeEnum.REGISTER, ComponentEnum.USER_PROFILE, user.getId());
//
//				mailService.sendWelcome(newUser);
//			} catch (Exception e) {
//				error("User role failed while registering", e);
//			}
//
//			return registeredUser.getId();
//		} catch (UserExistsException e) {
//			logger.error(e);
//			return new Long(SaveResult.FAIL);
//		}
//	}

	public List<FriendDTO> getRandomFriends(UserDTO owner) {
		if (owner == null) {
			return null;
		}

		try {
			List<FriendDTO> result = new ArrayList<FriendDTO>();
			User user = userDAOExt.get(owner.getId());
			List<UserFriend> acceptedfriends = userFriendDAO.listRandomFriends(
					user, Boolean.TRUE);
			for (UserFriend entity : acceptedfriends) {
				FriendDTO dto = new FriendDTO();
				User friend = entity.getFrom();
				dto.initFrom(friend);
				dto.setAvatar(friend.getAvatar());
				dto.setLoggined(LoginStatus.isOnline(entity.getFrom()
						.getUsername()));
				int totalEntries = userGoalEntryDAO.countEntries(friend.getId(), null) + userGoalEntryDoneDAO.countDoneEntries(friend.getId());
				int doingGoals = userGoalDAO.countGoals(friend.getId(), false);
				int finishedGoals = userGoalDAO.countGoals(friend.getId(), true);
				dto.setNumberOfAllEntries(new Integer(totalEntries));
				dto.setNumberDoingGoals(new Integer(doingGoals));
				dto.setNumberFinishedGoals(new Integer(finishedGoals));
				result.add(dto);
			}
			return result;
		} catch (ObjectRetrievalFailureException e) {
			logger.error(e);
			return null;
		}
	}

	public List<FriendDTO> getAllFriends(UserDTO owner) {
		if (owner == null) {
			return null;
		}

		try {
			List<FriendDTO> result = new ArrayList<FriendDTO>();
			User user = userDAOExt.get(owner.getId());
			List<UserFriend> acceptedfriends = userFriendDAO.listAllFriends(
					user, Boolean.TRUE);
			for (UserFriend entity : acceptedfriends) {
				FriendDTO dto = new FriendDTO();
				User friend = entity.getFrom();
				dto.initFrom(friend);
				dto.setAvatar(friend.getAvatar());
				dto.setLoggined(LoginStatus.isOnline(entity.getFrom()
						.getUsername()));
				result.add(dto);
			}
			return result;
		} catch (ObjectRetrievalFailureException e) {
			logger.error(e);
			return null;
		}
	}
	
	public List<FriendDTO> getFriends(String username, int pageId) {
		try {
			List<FriendDTO> result = new ArrayList<FriendDTO>();
			User user = (User) userDAOExt.loadUserByUsername(username);
			List<UserFriend> acceptedfriends = userFriendDAO.listFriends(user, pageId);
			for (UserFriend entity : acceptedfriends) {
				FriendDTO dto = new FriendDTO();
				User friend = entity.getFrom();
				dto.initFrom(friend);
				dto.setAvatar(friend.getAvatar());
				dto.setLoggined(LoginStatus.isOnline(entity.getFrom()
						.getUsername()));
				result.add(dto);
			}
			return result;
		} catch (ObjectRetrievalFailureException e) {
			logger.error(e);
			return null;
		}
	}
	
	public int countFriends(String username) {
		User user = (User) userDAOExt.loadUserByUsername(username);
		return userFriendDAO.countFriends(user, Boolean.TRUE);
	}

	public void addFriend(User person, User friend) {
		try {
			UserFriend userFriend = EntityFactory.createUserFriend(person,
					friend);
			if(!userFriendDAO.exists(person, friend)){
				userFriendDAO.save(userFriend);
			}
		} catch (Exception e) {
			logger.error(e);
		}
	}

	public void addFriend(UserDTO userRequest, Long friendId) {
		if (userRequest == null || friendId == null)
			return;

		try {
			User currentUser = userDAOExt.get(userRequest.getId());
			User friend = userDAOExt.get(friendId);
			if (friend != null) {
				addFriend(currentUser, friend);
				UserDTO toUser = new UserDTO();
				toUser.initFrom(friend);
				mailService.sendFriendRequest(userRequest, toUser);
			}
		} catch (ObjectRetrievalFailureException e) {
			logger.error(e);
		}
	}

	public void addFriends(UserDTO userRequest, Long[] friendIds) {
		if (friendIds != null) {
			for (Long friendId : friendIds) {
				addFriend(userRequest, friendId);
			}
		}
	}

	public UserStatusDTO getUserStatus(Long userId) {
		try {
			UserStatus userStatus = userStatusDAO.getLastestStatus(userId);
			if (userStatus == null)
				return null;

			UserStatusDTO dto = new UserStatusDTO();
			dto.initFrom(userStatus);
			return dto;
		} catch (ObjectRetrievalFailureException e) {
			debug("User status is not existed, create new one!");
			UserStatus userStatus = saveUserStatus(userId,
					"Click here to update your status...");
			UserStatusDTO dto = new UserStatusDTO();
			dto.initFrom(userStatus);
			return dto;
		}
	}

	public UserStatusDTO getUserStatus(UserDTO user) {
		if (user == null)
			return null;

		return getUserStatus(user.getId());
	}

	// Add status
	public UserStatus saveUserStatus(Long userId, String status) {
		UserStatus userStatus;

		User userEntity = userDAOExt.get(userId);
		userStatus = EntityFactory.createUserStatus(userEntity);
		userStatus.setUserStatus(status);
		userStatus = userStatusDAO.save(userStatus);

		updateEntity(userEntity);
		userDAOExt.save(userEntity);

		actionLoggerService.log(userEntity, userEntity, ActionTypeEnum.UPDATE,
				ComponentEnum.USER_STATUS, userStatus.getId());

		return userStatus;
	}

	public int saveUserStatus(UserDTO user, String status) {
		logger.debug("Save user's status");
		if (user == null || status == null) {
			logger.debug("Invalid session/user");
			return SaveResult.FAIL;
		}

		try {
			UserStatus userStatus = saveUserStatus(user.getId(), status);
			return userStatus != null ? SaveResult.SUCCESS : SaveResult.FAIL;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public List<FriendDTO> getMutualFriends(User owner, User friendOfFriend) {
		if (owner == null || friendOfFriend == null
				|| owner.equals(friendOfFriend)) {
			return null;
		}

		List<FriendDTO> result = new ArrayList<FriendDTO>();
		// if(!userFriendDAO.isFriend(owner, friendOfFriend)){
		// FriendDTO dto = new FriendDTO();
		// dto.initFrom(friendOfFriend);
		// dto.setLoggined(LoginStatus.isOnline(dto.getUsername()));
		// result.add(dto);
		// }

		Set<UserFriend> friendsOfFOF = friendOfFriend.getFriends();
		for (UserFriend friendOfFOF : friendsOfFOF) {
			User friend = friendOfFOF.getFrom();
			if (userFriendDAO.isFriend(owner, friend)) {
				FriendDTO dto = new FriendDTO();
				dto.initFrom(friend);
				dto.setLoggined(LoginStatus.isOnline(dto.getUsername()));
				int totalEntries = userGoalEntryDAO.countEntries(friend.getId(), null) + userGoalEntryDoneDAO.countDoneEntries(friend.getId());
				int doingGoals = userGoalDAO.countGoals(friend.getId(), false);
				int finishedGoals = userGoalDAO.countGoals(friend.getId(), true);
				dto.setNumberOfAllEntries(new Integer(totalEntries));
				dto.setNumberDoingGoals(new Integer(doingGoals));
				dto.setNumberFinishedGoals(new Integer(finishedGoals));
				result.add(dto);
			}

			// if(result.size() > 5) break;
		}

		return result;
	}

	public List<PotentialFriendDTO> getPeopleYouMayKnow(UserDTO owner,
			boolean onlineOnly) {
		if (owner == null)
			return null;
		User ur = userDAOExt.get(owner.getId());
		ur.setId(owner.getId());
		List<UserFriend> list = userFriendDAO.listPotentialFriends(ur, 5);
		
		Set<PotentialFriendDTO> result = new HashSet<PotentialFriendDTO>();
		for(UserFriend user: list) {
			PotentialFriendDTO dto = new PotentialFriendDTO();
			dto.setId(user.getFrom().getId());
			dto.setAvatar(user.getFrom().getAvatar());
			dto.setUsername(user.getFrom().getUsername());
			dto.setLoggined(LoginStatus.isOnline(dto.getUsername()));
			List<FriendDTO> mutualFriends = getMutualFriends(ur,user.getTo());
			dto.setMutualFriends(mutualFriends);

			result.add(dto);
		}
		
		
		/*try {
			User user = userDAOExt.get(owner.getId());
			Set<UserFriend> friends = user.getFriends();
			if (friends == null || friends.size() == 0) {
				return getAdmin(owner);
			}

			List<UserFriend> ownerUserFriends = new ArrayList<UserFriend>(
					friends);

			boolean stop = false;
			Random generator = new Random();
			Map<Integer, Object> checkedList = new HashMap<Integer, Object>();
			int count = 0;
			while (!stop && result.size() <= 5
					&& count < ownerUserFriends.size()) {
				int index = generator.nextInt(ownerUserFriends.size());

				if (checkedList.containsKey(index)) {
					continue;
				}

				checkedList.put(index, null);
				count++;

				// get random friend of owner
				UserFriend ownerUserFriend = ownerUserFriends.get(index);
				User friend = ownerUserFriend.getFrom();

				// get friend list of friend of owner
				Set<UserFriend> friendsOfFriend = friend.getFriends();
				Assert.notEmpty(friendsOfFriend);// at least have one (owner)

				for (UserFriend ufriendOfFriend : friendsOfFriend) {
					User friendOfFriend = ufriendOfFriend.getFrom();

					if (userFriendDAO.isFriend(user, friendOfFriend)
							|| user.equals(friendOfFriend)) {
						continue;
					}

					List<FriendDTO> mutualFriends = getMutualFriends(user,
							friendOfFriend);
					if (mutualFriends != null && mutualFriends.size() > 0) {
						PotentialFriendDTO dto = new PotentialFriendDTO();
						dto.setAvatar(friendOfFriend.getAvatar());
						dto.setUsername(friendOfFriend.getUsername());
						dto
								.setLoggined(LoginStatus.isOnline(dto
										.getUsername()));
						dto.setMutualFriends(mutualFriends);

						result.add(dto);
					}
				}
			}

		} catch (Exception e) {
			logger.error(e);
		}

		if (result.size() == 0) {
			return getAdmin(owner);
		}*/

		return new ArrayList<PotentialFriendDTO>(result);
	}

	public Map<String, List<FriendDTO>> getPeopleYouMayKnow(UserDTO owner) {
		if (owner == null)
			return null;
		try {
			User user = userDAOExt.get(owner.getId());
			List<UserFriend> ownerUserFriends = userFriendDAO.listAllFriends(
					user, Boolean.TRUE);
			if (ownerUserFriends == null || ownerUserFriends.size() == 0)
				return null;

			List<FriendDTO> mutualFriends = new ArrayList<FriendDTO>();
			Map<String, List<FriendDTO>> result = new HashMap<String, List<FriendDTO>>();
			Random generator = new Random();
			boolean stop = false;
			int maxTry = 5;
			while (!stop && maxTry > 0) {
				maxTry--;
				int index = generator.nextInt(ownerUserFriends.size());
				// get a random friend from owner friend list; name this friend
				// to [A]
				UserFriend randomOwnerUserFriend = ownerUserFriends.get(index);
				User randomOwnerFriend = randomOwnerUserFriend.getFrom();
				// get friend list of the above owner friend [A]
				List<UserFriend> rofUserFriends = userFriendDAO.listAllFriends(
						randomOwnerFriend, Boolean.TRUE);
				if (rofUserFriends != null && rofUserFriends.size() > 0) {
					index = generator.nextInt(rofUserFriends.size());
					// get a random friend from [A] friend list, name this
					// friend to [B]
					UserFriend randomROFUserFriend = rofUserFriends.get(index);
					User randomROFFriend = randomROFUserFriend.getFrom();
					// loop for the owner friend list
					for (UserFriend ownerUserFriend : ownerUserFriends) {
						User ownerFriend = ownerUserFriend.getFrom();
						// check [B] is friend of each of people in owner friend
						// list
						// if yes, add that people to mutual friend list
						if (userFriendDAO
								.isFriend(randomROFFriend, ownerFriend)) {
							FriendDTO friendDTO = new FriendDTO();
							friendDTO.initFrom(ownerFriend);
							friendDTO.setAvatar(ownerFriend.getAvatar());
							friendDTO.setLoggined(true);// TODO: remove dummy
							// settings
							mutualFriends.add(friendDTO);
						}
					}
					// At least, [B] has [A] in the mutual friend list
					Assert
							.notEmpty(mutualFriends,
									"[People you may know] function works not correct.");
					stop = true;
					result.put(randomROFFriend.getUsername(), mutualFriends);
				}
			}

			if (mutualFriends.size() == 0)
				return null;

			return result;
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}

	public int saveUserAvatar(Long userId, String avatarURL) {
		if (!userDAOExt.exists(userId)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Wrong user id");
				return SaveResult.FAIL;
			}
		}
		User user = userDAOExt.get(userId);
		user.setAvatar(avatarURL);
		updateEntity(user);
		userDAOExt.save(user);

		actionLoggerService.log(user, user, ActionTypeEnum.UPDATE,
				ComponentEnum.USER_AVATAR, user.getId());

		// update profile complete
		ProfileComplete profileComplete = profileCompleteDAO.findByPK(userId);
		if (profileComplete == null) {
			profileComplete = EntityFactory.createProfileComplete(user);
		}
		if (!profileComplete.getAvatar()) {
			profileComplete.setAvatar(true);
			profileCompleteDAO.save(profileComplete);
		}

		return SaveResult.SUCCESS;
	}

	public int saveUserAvatar(UserDTO user, String avatarURL) {
		if (user == null || StringUtils.isBlank(avatarURL)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Wrong session/user");
			}
			return SaveResult.FAIL;
		}

		return saveUserAvatar(user.getId(), avatarURL);
	}

	public int likeThisEntry(String username, Long entryId) {
		if (entryId < 0l)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			Assert.notNull(user, "Data failure.");

			UserGoalEntry entry = userGoalEntryDAO.get(entryId);
			
			int result = likeThisComponent(user, ComponentEnum.GOAL_ENTRY, entryId);
			if (SaveResult.SUCCESS == result) {
				actionLoggerService.log(entry.getUser(), user,
						ActionTypeEnum.LIKE, ComponentEnum.GOAL_ENTRY, entryId);
			}
			return result;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int likeThisDoneEntry(String username, Long entryId) {
		if (entryId < 0)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			Assert.notNull(user, "Data failure.");

			UserGoalDone entry = userGoalEntryDoneDAO.get(entryId);

			int result = likeThisComponent(user, ComponentEnum.DONE_ENTRY, entryId);
			if(SaveResult.SUCCESS == result){
				actionLoggerService.log(entry.getUser(), user, ActionTypeEnum.LIKE,
						ComponentEnum.DONE_ENTRY, entryId);
			}
			
			return result;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int likeThisComponent(User user, ComponentEnum component,
			Long componentId) {
		UserLike userLike = EntityFactory.createUserLike(user, component,
				componentId);
		try {
			if(!userLikeDAO.isLikedThis(user.getId(), component, componentId)){
				userLikeDAO.save(userLike);
				return SaveResult.SUCCESS;
			}else{
				logger.error("User["+user.getUsername()+"] requested double likes.");
				return SaveResult.FAIL;
			}
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int unlikeThisEntry(String username, Long entryId) {
		if (entryId < 0)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			Assert.notNull(user, "Data failure.");

			return unlikeThisComponent(user, ComponentEnum.GOAL_ENTRY, entryId);
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int unlikeThisDoneEntry(String username, Long entryId) {
		if (entryId < 0)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			Assert.notNull(user, "Data failure.");

			return unlikeThisComponent(user, ComponentEnum.DONE_ENTRY, entryId);
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int unlikeThisComponent(User user, ComponentEnum component,
			Long componentId) {
		try {
			UserLike userLike = userLikeDAO.findByInformation(user.getId(),
					component, componentId);
			if (userLike != null) {
				userLikeDAO.remove(userLike.getId());
				return SaveResult.SUCCESS;
			}
			return SaveResult.FAIL;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.igoal.service.UserManagerExt#saveUserBasicInfo(com.igoal.dto.UserDTO)
	 */
	public int saveUserBasicInfo(UserDTO user) {
		logger.debug("Save user's information");
		Assert.notNull(user, "Invalid session/user");
		// TODO: missing city
		try {
			UserInfo userInfo = userInfoDAO.get(user.getId());
			userInfo.setFirstName(user.getFirstName());
			userInfo.setMiddleName(user.getMiddleName());
			userInfo.setLastName(user.getLastName());
			userInfo.setBirthday(user.getBirthday());
			userInfo.setMale(user.isMale());
			userInfo.setMobile(user.getMobilePhone());
			userInfo.setPhone(user.getPhone());
			userInfo.setAddress(user.getAddress());
			userInfo.setUpdateDate(new Date());

			if (userInfoDAO.save(userInfo) != null) {
				if (StringUtils.isNotBlank(user.getFirstName())
						|| StringUtils.isNotBlank(user.getLastName())
						|| StringUtils.isNotBlank(user.getMiddleName())
						|| StringUtils.isNotBlank(user.getMobilePhone())
						|| StringUtils.isNotBlank(user.getPhone())
						|| StringUtils.isNotBlank(user.getAddress())) {
					User entity = userDAOExt.get(user.getId());
					ProfileComplete profileComplete = profileCompleteDAO
							.findByPK(entity.getId());
					if (profileComplete == null) {
						profileComplete = EntityFactory
								.createProfileComplete(entity);
					}
					if (!profileComplete.getBasicInfo()) {
						profileComplete.setBasicInfo(true);
						profileCompleteDAO.save(profileComplete);
					}

					updateEntity(entity);
					userDAOExt.save(entity);

					actionLoggerService.log(entity, entity,
							ActionTypeEnum.UPDATE, ComponentEnum.USER_PROFILE, user
									.getId());
				}

				return SaveResult.SUCCESS;
			}

			// User entity = userDAOExt.get(user.getId());
			// user.copyAttributesTo(entity);
			// UserInfo userInfo = entity.getUserInfo();
			// userInfo.setUpdateDate(new Date());
			// userInfoDAO.save(userInfo);
			// return SaveResult.SUCCESS;
		} catch (Exception e) {
			error("Save user's information unsuccessfully", e);
		}
		return SaveResult.FAIL;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.igoal.service.UserManagerExt#saveUserContactInfo(com.igoal.dto.UserDTO
	 * )
	 */
	public int saveUserContactInfo(UserDTO user) {
		logger.debug("Save user's information");
		Assert.notNull(user, "Invalid session/user");
		try {
			UserInfo userInfo = userInfoDAO.get(user.getId());
			userInfo.setYahoo(user.getYahoo());
			userInfo.setGoogle(user.getGoogle());
			userInfo.setSkype(user.getSkype());
			userInfo.setUpdateDate(new Date());

			userInfoDAO.save(userInfo);

			User userEntity = userInfo.getUser();
			updateEntity(userEntity);
			userDAOExt.save(userEntity);

			actionLoggerService.log(userInfo.getUser(), userInfo.getUser(),
					ActionTypeEnum.UPDATE, ComponentEnum.USER_PROFILE, user.getId());
			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error("Save user's information unsuccessfully", e);
		}
		return SaveResult.FAIL;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.igoal.service.UserManagerExt#saveUserJobInfo(com.igoal.dto.UserDTO)
	 */
	public int saveUserJobInfo(UserDTO user) {
		// TODO Auto-generated method stub
		return 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.igoal.service.UserManagerExt#saveUserHobbyInfo(com.igoal.dto.UserDTO)
	 */
	public int saveUserHobbyInfo(UserDTO user) {
		User u = (User) userDAOExt.loadUserByUsername(user.getUsername());
		Assert.notNull(u, "Data failure.");

		Set<Hobby> uh = u.getHobbies();
		uh = new HashSet<Hobby>();

		Map<String, Set<String>> hobbies = user.getHobbies();
		for (Entry<String, Set<String>> entry : hobbies.entrySet()) {
			String category = entry.getKey();
			Set<String> s = entry.getValue();
			for (String name : s) {
				Hobby h = hobbyDAO.getHobby(name, category);
				if (h == null) {
					h = hobbyDAO.createHobby(u, name, category);
				}
				uh.add(h);

				Set<User> hu = h.getUsers();
				if (hu == null) {
					hu = new HashSet<User>();
					h.setUsers(hu);
				}
				if (!hu.contains(uh)) {
					hu.add(u);
					hobbyDAO.save(h);
				}
			}
		}

		updateEntity(u);

		if (userDAOExt.saveUser(u) != null) {
			ProfileComplete profileComplete = profileCompleteDAO.findByPK(u
					.getId());
			if (profileComplete == null) {
				profileComplete = EntityFactory.createProfileComplete(u);
			}
			if (!profileComplete.getHobby()) {
				profileComplete.setHobby(true);
				profileCompleteDAO.save(profileComplete);
			}

			actionLoggerService.log(u, u, ActionTypeEnum.UPDATE,
					ComponentEnum.USER_PROFILE, u.getId());

			return SaveResult.SUCCESS;
		}

		return SaveResult.FAIL;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.igoal.service.UserManagerExt#saveUserPasswordInfo(com.igoal.dto.UserDTO
	 * )
	 */
	public int saveUserPasswordInfo(UserDTO user, String oldPassword,
			String newPassword) {
		// check old password
		String oldStr = passwordEncoder.encodePassword(oldPassword, null);
		if (user.getPassword().equals(oldStr)) {
			return savePassword(user, newPassword);
		}
		return SaveResult.FAIL;
	}

	public int saveAboutMe(String username, String aboutMe) {
		if (StringUtils.isBlank(aboutMe))
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			Assert.notNull(user, "Data failure.");

			UserInfo userInfo = user.getUserInfo();
			Assert.notNull(userInfo);
			aboutMe = aboutMe.replaceAll("\t", "").replaceAll("\n", "").trim();
			aboutMe = com.igoal.utils.StringUtils.removeHTMLTags(aboutMe);
			userInfo.setAboutMe(aboutMe);
			userInfoDAO.save(userInfo);

			updateEntity(user);
			userDAOExt.save(user);

			actionLoggerService.log(user, user, ActionTypeEnum.UPDATE,
					ComponentEnum.USER_PROFILE, user.getId());

			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.igoal.service.UserManagerExt#savePassword(com.igoal.dto.UserDTO,
	 * java.lang.String)
	 */
	public int savePassword(UserDTO userDTO, String password) {
		try {
			User user = userDAOExt.get(userDTO.getId());
			String p = passwordEncoder.encodePassword(password, null);
			user.setPassword(p);
			updateEntity(user);
			userDAOExt.save(user);
			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error("Save password unsuccessfully", e);
		}
		return SaveResult.FAIL;
	}

	public boolean checkPassword(String password) {
		try {
			String username = getUserInSession();
			User user = (User) userDAOExt.loadUserByUsername(username);
			Assert.notNull(user, "Data failure.");
			String pass = passwordEncoder.encodePassword(password, null);

			return user.getPassword().equals(pass);
		} catch (Exception e) {
			logger.error(e);
		}

		return false;
	}

	public boolean isFriend(UserDTO user1, UserDTO user2) {
		if (user1 == null || user2 == null)
			return false;

		User u1 = userDAOExt.get(user1.getId());
		User u2 = userDAOExt.get(user2.getId());

		return userFriendDAO.isFriend(u1, u2);
	}

	public boolean isWaitingAcceptFriend(UserDTO from, UserDTO to) {
		if (from == null || to == null)
			return false;

		User f = userDAOExt.get(from.getId());
		User t = userDAOExt.get(to.getId());

		UserFriend uf = userFriendDAO.findByFromTo(f, t);

		if (uf == null)
			return false;
		else
			return !uf.isAccepted();
	}

	public int acceptFriend(String from, String to) {
		if (StringUtils.isBlank(from) || StringUtils.isBlank(to))
			return SaveResult.FAIL;

		try {
			User f = (User) userDAOExt.loadUserByUsername(from);
			User t = (User) userDAOExt.loadUserByUsername(to);

			UserFriend uf = userFriendDAO.findByFromTo(f, t);

			if (uf != null) {
				uf.setAccepted(true);
				uf.setAcceptOn(new Date());
				uf.setUpdateDate(new Date());
				userFriendDAO.save(uf);

				UserFriend uf1 = userFriendDAO.findByFromTo(t, f);
				if (uf1 == null) {
					uf1 = EntityFactory.createUserFriend(t, f);
					uf1.setAccepted(true);
					uf1.setAcceptOn(new Date());
					uf1 = userFriendDAO.save(uf1);
					actionLoggerService.log(f, t, ActionTypeEnum.BE_FRIEND_WITH,
							ComponentEnum.FRIEND, uf1.getId());
				}else{
					uf1.setAccepted(true);
					uf1.setAcceptOn(new Date());
					uf1 = userFriendDAO.save(uf1);
				}
			}

			// send email to user
			Set<String> deliverMails = new HashSet<String>();
			deliverMails.add(f.getEmail());
			mailService.sendFriendAccept(f.getUsername(), t.getUsername(),
					deliverMails);

			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int refuseFriend(String from, String to) {
		if (StringUtils.isBlank(from) || StringUtils.isBlank(to))
			return SaveResult.FAIL;

		try {
			User f = (User) userDAOExt.loadUserByUsername(from);
			User t = (User) userDAOExt.loadUserByUsername(to);

			UserFriend uf = userFriendDAO.findByFromTo(f, t);

			if (uf != null) {
				userFriendDAO.remove(uf.getId());
				UserFriend uf1 = userFriendDAO.findByFromTo(t, f);
				if (uf1 != null) {
					userFriendDAO.remove(uf1.getId());
				}
			}

			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public List<FriendRequestDTO> getFriendRequests(String username) {
		Set<FriendRequestDTO> result = new HashSet<FriendRequestDTO>();

		try {
			User owner = (User) userDAOExt.loadUserByUsername(username);
			List<UserFriend> uFriends = userFriendDAO.listAllFriends(owner,
					false);
			if (uFriends != null) {
				for (UserFriend uFriend : uFriends) {
					FriendRequestDTO request = new FriendRequestDTO();
					request.initFrom(uFriend);
					result.add(request);
				}
			}
		} catch (Exception e) {
			logger.error(e);
		}
		return new ArrayList<FriendRequestDTO>(result);
	}

	public List<FriendDTO> getMutualFriends(UserDTO owner, UserDTO friend) {
		try {
			User userOwner = (User) userDAOExt.loadUserByUsername(owner
					.getUsername());
			User userFriend = (User) userDAOExt.loadUserByUsername(friend
					.getUsername());
			List<FriendDTO> result = getMutualFriends(userOwner, userFriend);
			return result;
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}

	public int addCategories(String username, Long[] categoryIds) {
		if (StringUtils.isBlank(username) || categoryIds == null
				|| categoryIds.length == 0)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);

			for (Long categoryId : categoryIds) {
				if (categoryId == null || categoryId <= 0)
					continue;

				Category category = categoryDAO.get(categoryId);
				if (category != null) {
					UserCategory userCategory = EntityFactory
							.createUserCategory(user, category);
					userCategoryDAO.save(userCategory);
				}
			}

			updateEntity(user);
			userDAOExt.save(user);

			return SaveResult.SUCCESS;

		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int removeCategories(String username, Long[] categoryIds) {
		if (StringUtils.isBlank(username) || categoryIds == null
				|| categoryIds.length == 0)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			for (Long categoryId : categoryIds) {
				if (categoryId == null || categoryId <= 0)
					continue;
				Category category = categoryDAO.get(categoryId);

				UserCategoryPk pk = new UserCategoryPk();
				pk.setCategory(category);
				pk.setUser(user);
				if (userCategoryDAO.exists(pk))
					userCategoryDAO.remove(pk);
			}

			updateEntity(user);
			userDAOExt.save(user);

			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public ProfileCompleteDTO getProfileComplete(UserDTO user) {
		ProfileComplete profileComplete = profileCompleteDAO.findByPK(user
				.getId());
		ProfileCompleteDTO dto = new ProfileCompleteDTO();
		if (profileComplete != null) {
			dto.initFrom(profileComplete);
		}
		return dto;
	}

	public List<JobDTO> getJobInfo(UserDTO user) {
		List<JobDTO> result = new ArrayList<JobDTO>();
		List<UserJob> userJobs = userJobDAO.getUserJobs(user.getId());
		for (UserJob userJob : userJobs) {
			JobDTO dto = new JobDTO();
			Job job = userJob.getJob();
			dto.initFrom(job);
			dto.setJobTitle(userJob.getTitle());
			dto.setFrom(userJob.getFromYear());
			dto.setTo(userJob.getToYear());
			result.add(dto);
		}
		return result;
	}

	public void saveJobInfo(UserDTO user, List<JobDTO> jobs) {
		User u = userDAOExt.get(user.getId());
		for (JobDTO job : jobs) {
			if (job.isRemoved()) {
				Job j = jobDAO.get(job.getId());
				UserJobId userJobId = new UserJobId(user.getId(), job.getId());
				UserJob userJob = userJobDAO.get(userJobId);
				u.getUserJobs().remove(userJob);
				j.getUserJobs().remove(userJob);
				userJobDAO.remove(userJob);
			} else {
				String jobName = job.getJobName();
				String jobTitle = job.getJobTitle();
				Integer jobFrom = job.getFrom();
				Integer jobTo = job.getTo();
				Job j = jobDAO.getJobByName(jobName);
				if (j == null) {
					j = EntityFactory.createJob(jobName);
					j = jobDAO.save(j);
				}
				if (j != null) {
					UserJob userJob = new UserJob();
					userJob.setId(new UserJobId(user.getId(), j.getId()));
					userJob.setJob(j);
					userJob.setUser(u);

					userJob.setTitle(jobTitle);
					userJob.setFromYear(jobFrom);
					userJob.setToYear(jobTo);

					u.getUserJobs().add(userJob);
					j.getUserJobs().add(userJob);

					userJobDAO.save(userJob);

					updateEntity(u);
					userDAOExt.save(u);
				} else {
					error("cannot create new job");
				}
			}
		}
	}

	public List<SchoolDTO> getSchoolInfo(UserDTO user) {
		List<SchoolDTO> result = new ArrayList<SchoolDTO>();
		List<UserSchool> userSchools = userSchoolDAO.getUserSchools(user
				.getId());
		for (UserSchool userSchool : userSchools) {
			SchoolDTO dto = new SchoolDTO();
			School job = userSchool.getSchool();
			dto.initFrom(job);
			dto.setFrom(userSchool.getFromYear());
			dto.setTo(userSchool.getToYear());
			result.add(dto);
		}
		return result;
	}

	@Deprecated
	public void saveSchoolInfo(UserDTO user, List<SchoolDTO> schools) {
//		User u = userDAOExt.get(user.getId());
//		for (SchoolDTO school : schools) {
//			if (school.isRemoved()) {
//				School s = schoolDAO.get(school.getId());
//				UserSchool userSchool = userSchoolDAO.get(userSchoolId);
//				u.getUserSchools().remove(userSchool);
//				s.getUserSchools().remove(userSchool);
//				userSchoolDAO.remove(userSchool);
//			} else {
//				String schoolName = school.getName();
//				Integer schoolFrom = school.getFrom();
//				Integer schoolTo = school.getTo();
//				School j = schoolDAO.getSchoolByName(schoolName);
//				if (j == null) {
//					j = EntityFactory.createSchool(schoolName);
//					j = schoolDAO.save(j);
//				}
//				if (j != null) {
//					UserSchool userSchool = new UserSchool();
//					userSchool.setId(new UserSchoolPk(user.getId(), j.getId()));
//					userSchool.setSchool(j);
//					userSchool.setUser(u);
//
//					userSchool.setFromYear(schoolFrom);
//					userSchool.setToYear(schoolTo);
//
//					u.getUserSchools().add(userSchool);
//					j.getUserSchools().add(userSchool);
//
//					userSchoolDAO.save(userSchool);
//
//					updateEntity(u);
//					userDAOExt.save(u);
//				} else {
//					error("cannot create new school");
//				}
//			}
//		}
	}

	public boolean isDisable(String username) {
		// check user's password expire
		try{
			User user = (User) userDAOExt.loadUserByUsername(username);
			if (user != null) {
				return !user.isCredentialsNonExpired();
			}
			return true;
		}catch (UsernameNotFoundException e) {
			logger.error(e);
			return false;
		}
	}

	public boolean checkActivationCode(String username, String code) {
		User user = (User) userDAOExt.loadUserByUsername(username);
		String activateCode = user.getActivationCode();
		return code.equals(activateCode);
	}

	public int saveActionvationCode(UserDTO userDTO, String code) {
		try {
			User user = userDAOExt.get(userDTO.getId());
			user.setActivationCode(code);
			updateEntity(user);
			userDAOExt.save(user);
			return SaveResult.SUCCESS;
		} catch (Exception e) {
			error("Save activation code unsuccessfully", e);
		}
		return SaveResult.FAIL;
	}

	public int setCredentialsExpired(UserDTO userDTO, boolean expired) {
		try {
			User user = userDAOExt.get(userDTO.getId());
			user.setCredentialsExpired(expired);
			userDAOExt.save(user);
			return SaveResult.SUCCESS;
		} catch (Exception e) {
			error("enableCredential unsuccessfully", e);
		}
		return SaveResult.FAIL;
	}

	public List<PotentialFriendDTO> getAdmin(UserDTO userDTO) {
		List<PotentialFriendDTO> result = new ArrayList<PotentialFriendDTO>();
		try {
			User user = userDAOExt.get(userDTO.getId());

			Role admin = roleDAO.getRoleAdmin();
			Set<User> adminUsers = admin.getUsers();
			if (adminUsers != null) {
				int count = 0;
				for (User adminUser : adminUsers) {
					if (count > 5) {
						break;
					}

					if (userFriendDAO.isFriend(user, adminUser))
						continue;

					count++;

					List<FriendDTO> mutualFriends = getMutualFriends(user,
							adminUser);

					PotentialFriendDTO dto = new PotentialFriendDTO();
					dto.setAvatar(adminUser.getAvatar());
					dto.setUsername(adminUser.getUsername());
					dto.setLoggined(LoginStatus.isOnline(dto.getUsername()));
					dto.setMutualFriends(mutualFriends);

					result.add(dto);
				}
			}

		} catch (Exception e) {
			logger.error(e);
		}

		return result;
	}

	public int thumbUserStatus(String username, Long statusId, boolean up) {
		if (StringUtils.isBlank(username) || statusId < 0L) {
			return SaveResult.FAIL;
		}

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			UserStatus userStatus = userStatusDAO.get(statusId);

			if (userStatus == null) {
				logger.info("Cannot find status by id: " + statusId);
				return SaveResult.FAIL;
			}

			UserThumb userThumb = userThumbDAO.findByUserAndComponent(user,
					ComponentEnum.USER_STATUS, userStatus.getId());

			if (userThumb == null) {
				userThumb = EntityFactory.createUserThumb(user,
						ComponentEnum.USER_STATUS, statusId);
			}

			userThumb.setUp(up);
			userThumbDAO.save(userThumb);

			if (up) {
				actionLoggerService.log(userStatus.getUser(), user,
						ActionTypeEnum.THUMBUP, ComponentEnum.USER_STATUS,
						userStatus.getId());
			}

			updateEntity(user);
			userDAOExt.save(user);

			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int thumbUpUserStatus(String username, Long statusId) {
		return thumbUserStatus(username, statusId, true);
	}

	public int thumbDownUserStatus(String username, Long statusId) {
		return thumbUserStatus(username, statusId, false);
	}

	public Boolean getUserThumbedStatus(String username, Long statusId) {
		if (StringUtils.isBlank(username) || statusId < 0L) {
			return null;
		}

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			UserStatus userStatus = userStatusDAO.get(statusId);

			if (userStatus == null) {
				logger.info("Cannot find status by id: " + statusId);
				return null;
			}

			UserThumb userThumb = userThumbDAO.findByUserAndComponent(user,
					ComponentEnum.USER_STATUS, userStatus.getId());
			if (userThumb == null)
				return null;

			return userThumb.isUp();
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}

	public int countUserStatusThumbed(Long statusId, boolean up) {
		if (statusId < 0L) {
			return 0;
		}

		try {
			UserStatus userStatus = userStatusDAO.get(statusId);

			if (userStatus == null) {
				logger.info("Cannot find status by id: " + statusId);
				return 0;
			}

			return userThumbDAO.countThumb(ComponentEnum.USER_STATUS,
					userStatus.getId(), up);
		} catch (Exception e) {
			logger.error(e);
			return 0;
		}
	}

	public int countUserStatusThumbedUp(Long statusId) {
		return countUserStatusThumbed(statusId, true);
	}

	public int countUserStatusThumbedDown(Long statusId) {
		return countUserStatusThumbed(statusId, false);
	}

	public int markReadNotification(String username, Long[] notificationIds) {
		if (StringUtils.isBlank(username))
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			userActivityDAO.markReadNotificationLog(user);

			updateEntity(user);
			userDAOExt.save(user);

			return SaveResult.SUCCESS;
		} catch (Exception e) {
			error(e);
			return SaveResult.FAIL;
		}
	}
	
	public int cheerThisEntry(String username, Long entryId) {
		if (entryId < 0l)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			Assert.notNull(user, "Data failure.");

			UserGoalEntry entry = userGoalEntryDAO.get(entryId);
			
			int result = cheerThisComponent(user, ComponentEnum.GOAL_ENTRY, entryId);
			if (SaveResult.SUCCESS == result) {
				actionLoggerService.log(entry.getUser(), user,
						ActionTypeEnum.CHEER, ComponentEnum.GOAL_ENTRY, entryId);
			}
			return result;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int cheerThisDoneEntry(String username, Long entryId) {
		if (entryId < 0)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAOExt.loadUserByUsername(username);
			Assert.notNull(user, "Data failure.");

			UserGoalDone entry = userGoalEntryDoneDAO.get(entryId);

			int result = cheerThisComponent(user, ComponentEnum.DONE_ENTRY, entryId);
			if(SaveResult.SUCCESS == result){
				actionLoggerService.log(entry.getUser(), user, ActionTypeEnum.CHEER,
						ComponentEnum.DONE_ENTRY, entryId);
			}
			
			return result;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int cheerThisComponent(User user, ComponentEnum component, Long componentId) {
		UserCheer userCheer = EntityFactory.createUserCheer(user, component, componentId);
		try {
			if (!userCheerDAO.isCheerThis(user.getId(), component, componentId)) {
				userCheerDAO.save(userCheer);
				return SaveResult.SUCCESS;
			} else {
				logger.error("User[" + user.getUsername() + "] requested double cheers.");
				return SaveResult.FAIL;
			}
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List<FriendRequestDTO> getFriendRequestsByPage(String username, Date lastOrderPropertyValue, String orderProperty, boolean before){
		List<FriendRequestDTO> result = new ArrayList<FriendRequestDTO>();
		
		try{
			User owner = (User) userDAOExt.loadUserByUsername(username);
			Map criteria = new HashMap();
			criteria.put("to", owner);
			criteria.put("accepted", Boolean.FALSE);
			criteria.put("status", DataStatus.USING);
			List<UserFriend> userFriendRequests = userFriendDAO.pagingDependOnOrderProperty(criteria, orderProperty, lastOrderPropertyValue, before, Constants.MAX_USER_NOTIFICATION);
			
			if(userFriendRequests != null){
				for (UserFriend uFriend : userFriendRequests) {
					FriendRequestDTO request = new FriendRequestDTO();
					request.initFrom(uFriend);
					result.add(request);
				}
			}
		}catch (Exception e) {
			logger.error(e);
		}
		
		return result;
	}

	public int countNewFriendRequests(String username) {
		try{
			User owner = (User) userDAOExt.loadUserByUsername(username);
			return userFriendDAO.countFriends(owner, Boolean.FALSE);
		}catch (Exception e) {
			logger.error(e);			
		}
		return 0;
	}
	
	public int refuseSilenceFriend(String from, String to) {
		if (StringUtils.isBlank(from) || StringUtils.isBlank(to))
			return SaveResult.FAIL;

		try {
			User f = (User) userDAOExt.loadUserByUsername(from);
			User t = (User) userDAOExt.loadUserByUsername(to);

			UserFriend userFriend = EntityFactory.createUserFriend(f, t);
			if (!userFriendDAO.exists(f, t)) {
				userFriendDAO.save(userFriend);
			}

			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}
	
	public long addWallPost(String postOwner, String wallOwner, String thought, String privacy) {
		// TODO process html for thought?

		if (StringUtils.isBlank(thought)) {
			return SaveResult.FAIL;
		}

		try {
			User postUser = (User) userDAOExt.loadUserByUsername(postOwner);
			User wallUser = (User) userDAOExt.loadUserByUsername(wallOwner);

			if (postUser == null || wallUser == null) {
				return SaveResult.FAIL;
			}

			UserThought userThought = EntityFactory.createWallPost(postUser, wallUser);

			PrivacySetting privacySetting = privacySettingDAO.getPrivacy(PrivacyEnum
					.valueOf(privacy));
			userThought.setPrivacy(privacySetting);

			userThought.setPost(thought);
			userThought = userThoughtDAO.save(userThought);

			updateEntity(postUser);
			userDAOExt.save(postUser);

			logger.info(postUser.getUsername() + " save thought: " + thought);
			actionLoggerService.log(wallUser, postUser, ActionTypeEnum.NEW,
					ComponentEnum.USER_THOUGHT, userThought.getId(), privacySetting);

			return userThought.getId();
		} catch (Exception e) {
			logger.error(e);
		}

		return SaveResult.FAIL;
	}

	public int deleteWallPost(Long id, String whoDelete) {
		UserThought post = userThoughtDAO.get(id);
		if (whoDelete.equals(post.getPostOwner().getUsername())
				|| whoDelete.equals(post.getWallOwner().getUsername())) {
			userThoughtDAO.deletePost(id);
			wallUpdateDAO.removeFromWall(ComponentEnum.USER_THOUGHT, id);
			return SaveResult.SUCCESS;
		}
		return SaveResult.FAIL;
	}
	
	public List<UserDTO> getUserHavingRole(String roleName){
		Role role = roleDAO.getRoleByName(roleName);
		if(role != null){
			List<UserDTO> result = new ArrayList<UserDTO>();
			for(User user : role.getUsers()){
				UserDTO userDTO = new UserDTO();
				userDTO.initFrom(user);
				result.add(userDTO);
			}
			return result;
		}
		
		return null;
	}
	
	public UserDTO saveSubcriptionType(String username, Integer subscriptionType, boolean receiveSubscription){
		try{
			User user = (User) userDAOExt.loadUserByUsername(username);
			user.setSubscriptionType(subscriptionType);
			user.setReceiveSubscription(receiveSubscription);
			UserDTO userDTO = new UserDTO();
			userDTO.initFrom(user);
			return userDTO;
		}catch (UsernameNotFoundException e) {
			return null;
		}
	}
}
